﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;

using System.Linq;
using System.Linq.Expressions;

using ProjectBase.Core;

using $prjsn$.Common;
using $prjsn$.Common.Validation;

namespace $safeprojectname$
{
    public class ReferenceHasRequiredSettingsRule<TItem, TReference> : RuleBase, IValidationRule<TItem>
        where TItem : class
        where TReference : class
    {
        #region members
        private readonly Func<TItem, TReference> _compiledExpression;
        Func<TReference, bool> _predicate;
        Func<TItem, TReference, bool> _predicateWithEntity;
        string _requiredSetting;
        #endregion members

        #region Constructor
        public ReferenceHasRequiredSettingsRule(Expression<Func<TItem, TReference>> expression)
        {
            var body = expression.Body as MemberExpression;
            if (body.IsNull())
            {
                throw new NotSupportedException(
                    "Provided 'Expression<Func<TITem, TReference>>' must be of a 'ExpressionType.MemberAccess' (ReferenceHasRequiredSettingsRule). ");
            }
            PropertyName = body.Member.Name;
            _compiledExpression = expression.Compile();

        }
        #endregion Constructor

        #region Validate

        public virtual ValidationResult Validate(TItem item, out ValidationMessage message)
        {
            if (Predicate.IsNull() && PredicateWithEntity.IsNull())
            {
                throw new ArgumentException("Rule was not correctly set, missing 'Predicate' delegate");
            }

            message = null;

            // item cannot be null
            if (item.IsNull())
            {
                message = new ValidationMessage(Str.Messages.NullInsteadOfEntityProvided1)
                {
                    EntityName,
                };
                return ValidationResult.Error;
            }

            var entity = item as IPersistentObject;
            var itemId = entity.IsNull()
                ? "N/A"
                : entity.IsTransient() ? "new" : entity.IDToDisplay;

            var reference = CompiledExpression.Invoke(item);

            if (reference.Is())
            {
                // Is Predicate condition met
                var hasRequiredSettings = Predicate.Is()
                    ? Predicate(reference)
                    : PredicateWithEntity(item, reference)
                    ;

                if (hasRequiredSettings)
                {
                    return ValidationResult.IsValid;
                }
                
                message = new ValidationMessage(Str.Messages.ValidationReferenceHasIncorrectSetting3)
                {
                    itemId,
                    PropertyName.GetLocalized(EntityName),
                    RequiredSetting
                };
                
                return ValidationResult.Error;
            }


            // referenced property is NULL
            if (!IsRequired)
            {
                return ValidationResult.IsValid;
            }

            message = new ValidationMessage(Str.Messages.MissingProperty2)
            {
                itemId,
                PropertyName.GetLocalized(EntityName),
            };
            return ValidationResult.Error;
        }

        #endregion Validate

        #region fluent
        public virtual ReferenceHasRequiredSettingsRule<TItem, TReference> HasSetting(
            Func<TReference, bool> predicate)
        {
            _predicate = predicate;
            return this;
        }
        public virtual ReferenceHasRequiredSettingsRule<TItem, TReference> HasSetting(
            Func<TItem, TReference, bool> predicateWithEntity)
        {
            _predicateWithEntity = predicateWithEntity;
            return this;
        }
        public virtual ReferenceHasRequiredSettingsRule<TItem, TReference> WithErrorMessage(string requiredSetting)
        {
            _requiredSetting = requiredSetting;
            return this;
        }
        public virtual ReferenceHasRequiredSettingsRule<TItem, TReference> AllowNull()
        {
            IsRequired = false;
            return this;
        }

        #endregion fluent

        #region properties
        protected virtual Func<TItem, TReference> CompiledExpression { get { return _compiledExpression; } }
        protected virtual Func<TReference, bool> Predicate { get { return _predicate; } }
        protected virtual Func<TItem, TReference, bool> PredicateWithEntity { get { return _predicateWithEntity; } }
        protected virtual string RequiredSetting { get { return _requiredSetting; } }
        public virtual bool IsRequired { get; set; }

        protected virtual string EntityName { get { return typeof(TItem).Name; } }
        #endregion properties
    }
}